<?php

/**
 * 缓存服务
 *        'osc' => array(
  //            array(
  //                'host' => '2b775095126611e4.m.cnhzalicm10pub001.ocs.aliyuncs.com',
  //                'port' => 11211,
  //                'weight' => 60,
  //                'username' => '2b775095126611e4',
  //                'password' => 'Sefsw24104445',
  //            ),
  //        ),
 */
class OSCMemcached extends CCache {

    /**
     * @var boolean whether to use memcached or memcache as the underlying caching extension.
     * If true {@link http://pecl.php.net/package/memcached memcached} will be used.
     * If false {@link http://pecl.php.net/package/memcache memcache}. will be used.
     * Defaults to false.
     */
    public $useMemcached = true;

    /**
     * @var Memcache the Memcache instance
     */
    private $_cache = null;

    /**
     * @var array list of memcache server configurations
     */
    private $_servers = array();
    //osc缓存
    private $_osc = array();

    /**
     * Initializes this application component.
     * This method is required by the {@link IApplicationComponent} interface.
     * It creates the memcache instance and adds memcache servers.
     * @throws CException if memcache extension is not loaded
     */
    public function init() {
        parent::init();
        if (!class_exists('Memcached', false)) {
            $this->useMemcached = false;
        }
        $servers = $this->getServers();
        $cache = $this->getMemCache();
        if (count($servers)) {
            foreach ($servers as $server) {
                if ($this->useMemcached) {
                    $cache->addServer($server->host, $server->port, $server->weight);
                    $cache->setSaslAuthData($server->username, $server->password);
                } else
                    $cache->addServer($server->host, $server->port, $server->persistent, $server->weight, $server->timeout, $server->retryInterval, $server->status);
            }
        } else
            $cache->addServer('localhost', 11211);
    }

    /**
     * @throws CException if extension isn't loaded
     * @return Memcache|Memcached the memcache instance (or memcached if {@link useMemcached} is true) used by this component.
     */
    public function getMemCache() {
        if ($this->_cache !== null)
            return $this->_cache;
        else {
            $extension = $this->useMemcached ? 'memcached' : 'memcache';
            if (!extension_loaded($extension))
                throw new CException(Yii::t('yii', "CMemCache requires PHP {extension} extension to be loaded.", array('{extension}' => $extension)));
            $this->_cache = $this->useMemcached ? new Memcached : new Memcache;
            if ($this->useMemcached) {
                $this->_cache->setOption(Memcached::OPT_COMPRESSION, false); //关闭压缩功能
                $this->_cache->setOption(Memcached::OPT_BINARY_PROTOCOL, true); //使用binary二进制协议
            }
            return $this->_cache;
        }
    }

    /**
     * @return array list of memcache server configurations. Each element is a {@link CMemCacheServerConfiguration}.
     */
    public function getServers() {
        if ($this->useMemcached) {
            return $this->_osc;
        }
        return $this->_servers;
    }

    /**
     * @param array $config list of memcache server configurations. Each element must be an array
     * with the following keys: host, port, persistent, weight, timeout, retryInterval, status.
     * @see http://www.php.net/manual/en/function.Memcache-addServer.php
     */
    public function setServers($config) {
        foreach ($config as $c)
            $this->_servers[] = new CMemCacheServerConfiguration($c);
    }

    public function setOsc($config) {
        foreach ($config as $c)
            $this->_osc[] = new CMemCacheServerConfiguration($c);
    }

    /**
     * Retrieves a value from cache with a specified key.
     * This is the implementation of the method declared in the parent class.
     * @param string $key a unique key identifying the cached value
     * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
     */
    protected function getValue($key) {
        return $this->_cache->get($key);
    }

    /**
     * Retrieves multiple values from cache with the specified keys.
     * @param array $keys a list of keys identifying the cached values
     * @return array a list of cached values indexed by the keys
     */
    protected function getValues($keys) {
        return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys);
    }

    /**
     * Stores a value identified by a key in cache.
     * This is the implementation of the method declared in the parent class.
     *
     * @param string $key the key identifying the value to be cached
     * @param string $value the value to be cached
     * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
     * @return boolean true if the value is successfully stored into cache, false otherwise
     */
    protected function setValue($key, $value, $expire) {
        if ($expire > 0)
            $expire+=time();
        else
            $expire = 0;

        return $this->useMemcached ? $this->_cache->set($key, $value, $expire) : $this->_cache->set($key, $value, 0, $expire);
    }

    /**
     * Stores a value identified by a key into cache if the cache does not contain this key.
     * This is the implementation of the method declared in the parent class.
     *
     * @param string $key the key identifying the value to be cached
     * @param string $value the value to be cached
     * @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
     * @return boolean true if the value is successfully stored into cache, false otherwise
     */
    protected function addValue($key, $value, $expire) {
        if ($expire > 0)
            $expire+=time();
        else
            $expire = 0;

        return $this->useMemcached ? $this->_cache->add($key, $value, $expire) : $this->_cache->add($key, $value, 0, $expire);
    }

    /**
     * Deletes a value with the specified key from cache
     * This is the implementation of the method declared in the parent class.
     * @param string $key the key of the value to be deleted
     * @return boolean if no error happens during deletion
     */
    protected function deleteValue($key) {
        return $this->_cache->delete($key, 0);
    }

    /**
     * Deletes all values from cache.
     * This is the implementation of the method declared in the parent class.
     * @return boolean whether the flush operation was successful.
     * @since 1.1.5
     */
    protected function flushValues() {
        return $this->_cache->flush();
    }

}

/**
 * CMemCacheServerConfiguration represents the configuration data for a single memcache server.
 *
 * See {@link http://www.php.net/manual/en/function.Memcache-addServer.php}
 * for detailed explanation of each configuration property.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @package system.caching
 * @since 1.0
 */
class CMemCacheServerConfiguration extends CComponent {

    /**
     * @var string memcache server hostname or IP address
     */
    public $host;

    /**
     * @var integer memcache server port
     */
    public $port = 11211;

    /**
     * @var boolean whether to use a persistent connection
     */
    public $persistent = true;

    /**
     * @var integer probability of using this server among all servers.
     */
    public $weight = 1;

    /**
     * @var integer value in seconds which will be used for connecting to the server
     */
    public $timeout = 15;

    /**
     * @var integer how often a failed server will be retried (in seconds)
     */
    public $retryInterval = 15;

    /**
     * @var boolean if the server should be flagged as online upon a failure
     */
    public $status = true;
    public $username = NULL;
    public $password = NULL;

    /**
     * Constructor.
     * @param array $config list of memcache server configurations.
     * @throws CException if the configuration is not an array
     */
    public function __construct($config) {
        if (is_array($config)) {
            foreach ($config as $key => $value)
                $this->$key = $value;
            if ($this->host === null)
                throw new CException(Yii::t('yii', 'CMemCache server configuration must have "host" value.'));
        } else
            throw new CException(Yii::t('yii', 'CMemCache server configuration must be an array.'));
    }

}
